﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Log;
using mepostras.BLL;
using mepostras.BO.Util;
using mepostras.DAL;
using mepostras.Settings.BusinessLayer;
using GeneralCode = mepostras.BO.Util.BoConfig.MessageCode.General;

namespace mepostras.BO
{
    public class ArticleBo : IArticleBo
    {
        /// <summary>
        /// Render alias dựa vào title
        /// </summary>
        /// <param name="title"></param>
        /// <param name="currentAlias"></param>
        /// <returns></returns>
        private string RenderAlias(string title, string currentAlias)
        {
            var alias = BoCommon.ConvertToUnSign(title);

            // Nếu alias mới render trùng với alias hiện tại thì ko cần render nữa
            if (alias == currentAlias && !string.IsNullOrEmpty(currentAlias))
            {
                return alias;
            }

            int index = 0;

            // Lặp cho đến khi nào có alias là unique
            while (true)
            {
                if (Get(alias) == null)
                {
                    break;
                }

                index++;
                alias = BoCommon.ConvertToUnSign(String.Format("{0}-{1}", title, index));
            }

            return alias;
        }

        /// <summary>
        /// Decode để hiển thị ra Html
        /// </summary>
        /// <param name="article"></param>
        /// <returns></returns>
        private void HtmlDecode(Article article)
        {
            if (article != null)
            {
                article.Title = HttpUtility.HtmlDecode(article.Title);
                article.Summary = HttpUtility.HtmlDecode(article.Summary);
                article.Description = HttpUtility.HtmlDecode(article.Description);
                article.Thumbnail = HttpUtility.HtmlDecode(article.Thumbnail);
            }
        }

        /// <summary>
        /// Encode để hiển thị ra Html
        /// </summary>
        /// <param name="article"></param>
        /// <returns></returns>
        private void HtmlEncode(Article article)
        {
            if (article != null)
            {
                article.Title = HttpUtility.HtmlEncode(article.Title);
                article.Summary = HttpUtility.HtmlEncode(article.Summary);
                article.Description = HttpUtility.HtmlEncode(article.Description);
                article.Thumbnail = HttpUtility.HtmlEncode(article.Thumbnail);
            }
        }

        public string RenderLocalId(int id)
        {
            return String.Format(BoConfig.Format.LocalId, BoConfig.Prefix.Article, id);
        }

        public bool Insert(Article article, out string messageCode)
        {
            messageCode = GeneralCode.ServerError;
            bool result = false;
            try
            {
                article.CreatedDate = article.UpdatedDate = DateTime.Now.Date;

                // Tạo alias
                article.Alias = RenderAlias(article.Title, string.Empty);

                HtmlEncode(article);

                // Lấy kết quả trả về
                result = DataRepository.ArticleProvider.Insert(article);

                // Nếu thành công thì cập nhật LocalId
                if (result)
                {
                    article.LocalId = RenderLocalId(article.Id);
                    DataRepository.ArticleProvider.Update(article);
                }

                HtmlDecode(article);
                // Dựa vào kết quả trả về mà set mã lỗi
                messageCode = result ? GeneralCode.InsertSuccess : GeneralCode.InsertFail;
            }
            catch (Exception ex)
            {
                SingletonLogger.Instance.Error("Insert", ex);
            }
            return result;
        }

        public bool Update(Article article, out string messageCode)
        {
            messageCode = GeneralCode.ServerError;
            bool result = false;
            try
            {
                article.UpdatedDate = DateTime.Now.Date;

                // Tạo alias
                article.Alias = RenderAlias(article.Title, article.Alias);

                HtmlEncode(article);

                // Nếu chưa có LocalId thì update nó luôn
                if (string.IsNullOrEmpty(article.LocalId))
                {
                    article.LocalId = RenderLocalId(article.Id);
                }

                // Lấy kết quả trả về
                result = DataRepository.ArticleProvider.Update(article);

                HtmlDecode(article);
                // Dựa vào kết quả trả về mà set mã lỗi
                messageCode = result ? GeneralCode.UpdateSuccess : GeneralCode.UpdateFail;
            }
            catch (Exception ex)
            {
                SingletonLogger.Instance.Error("Update", ex);
            }
            return result;
        }

        public bool Save(Article article, out string messageCode)
        {
            messageCode = GeneralCode.ServerError;
            bool result = false;
            try
            {
                var tmpArticle = Get(article.Id);
                result = tmpArticle == null ? Insert(article, out messageCode) : Update(article, out messageCode);
            }
            catch (Exception ex)
            {
                SingletonLogger.Instance.Error("Update", ex);
            }
            return result;
        }

        public bool Delete(Article article, out string messageCode)
        {
            messageCode = GeneralCode.ServerError;
            bool result = false;
            try
            {
                // Không tìm thấy item
                if (article == null)
                {
                    messageCode = GeneralCode.NotFoundItem;
                    goto StepResult;
                }

                // Lấy kết quả trả về
                result = DataRepository.ArticleProvider.Delete(article.Id);

                // Dựa vào kết quả trả về mà set mã lỗi
                messageCode = result ? GeneralCode.DeleteSuccess : GeneralCode.DeleteFail;
            }
            catch (Exception ex)
            {
                SingletonLogger.Instance.Error("Delete", ex);
            }

        StepResult:
            return result;
        }

        public bool Delete(int articleId, out string messageCode)
        {
            return Delete(DataRepository.ArticleProvider.GetById(articleId), out messageCode);
        }

        public Article GetAbout(string language)
        {
            // Lấy about dựa vào menu alias
            // Tùy vào language mà lấy menu tiếng nào
            // Hiện tại chỉ làm tiếng Việt nên cái ngôn ngữ chưa cần
            // Alias thì fixed cứng
            string alias = language == BoConfig.Language.Vietnamese
                               ? ServiceFacade.SettingsHelper.HomeAbout
                               : ServiceFacade.SettingsHelper.HomeEnglishAbout;

            int count;

            var menu = DataRepository.MenuProvider.GetPaged(String.Format("Alias = '{0}'", alias), string.Empty, 0, 1, out count).FirstOrDefault();

            if (menu == null)
            {
                return null;
            }

            var article = DataRepository.ArticleProvider.GetByMenuId(menu.Id).FirstOrDefault(m => m.IsPublished);
            HtmlDecode(article);
            return article;
        }

        public TList<Article> GetHomeLatestNews(int? number, string language)
        {
            int count;
            int length = number ?? ServiceFacade.SettingsHelper.HomeNewsNumber;
            return
                DataRepository.ArticleProvider.GetPaged(
                    String.Format("IsPublished = 1 AND Language = '{0}'", language), "PublishedDate DESC", 0, length,
                    out count);
        }

        public TList<Article> GetNews(string alias, string language, ref int? page, out bool isMore, out Menu menu)
        {
            isMore = false;
            int count,
                length = ServiceFacade.SettingsHelper.ArticleNewsNumber,
                currentPage = page == null || page == 0 ? 1 : page.Value;
            int start = currentPage - 1;
            page = currentPage;

            menu = DataRepository.MenuProvider.GetPaged(String.Format("Alias = '{0}'", alias), string.Empty, 0, 1, out count).FirstOrDefault();
            if (menu == null)
            {
                return new TList<Article>();
            }

            var articles =
                DataRepository.ArticleProvider.GetPaged(
                    String.Format(
                        "MenuId = {0} AND IsPublished = 1 AND Language = '{1}' AND PublishedDate < '{2:yyyy-MM-dd}'",
                        menu.Id, language, DateTime.Now),
                    "PublishedDate DESC", start, length, out count);

            // Nếu số bài viết thực được lấy ra bằng số bài trong 1 trang thì còn nữa
            isMore = length <= articles.Count;

            return articles;
        }

        public void GetRelatedNews(int articleId, int? menuId, DateTime? publishedDate, out TList<Article> newerArticles, 
            out TList<Article> olderArticles)
        {
            int count;
            DateTime date = publishedDate.HasValue ? publishedDate.Value : DateTime.Now;
            menuId = menuId.HasValue ? menuId.Value : 0;

            // Phải dùng biến tmp vì biến ref, out ko xài được hàm Exists
            var tmpNewerArticles =
                DataRepository.ArticleProvider.GetPaged(
                    String.Format(
                        "MenuId = {0} AND Id <> {1} AND IsPublished = 1 AND PublishedDate >= '{2:yyyy-MM-dd}' AND PublishedDate < '{3:yyyy-MM-dd}'",
                        menuId, articleId, date, DateTime.Now), "PublishedDate DESC", 0,
                    ServiceFacade.SettingsHelper.RelatedArticleNewsNumber, out count);
            newerArticles = tmpNewerArticles;

            // Lấy danh sách tin cũ gấp đôi để sau đó remove mấy tin đã có trong newer
            // vì nếu cùng ngày thì nó có thể xảy ra trường hợp 1 bài có trong cả 2 list
            olderArticles =
                DataRepository.ArticleProvider.GetPaged(
                    String.Format(
                        "MenuId = {0} AND Id <> {1} AND IsPublished = 1 AND PublishedDate <= '{2:yyyy-MM-dd}' AND PublishedDate < '{3:yyyy-MM-dd}'",
                        menuId, articleId, date, DateTime.Now), "PublishedDate DESC", 0,
                    ServiceFacade.SettingsHelper.RelatedArticleNewsNumber*2, out count);
            olderArticles =
                olderArticles.FindAll(article => !tmpNewerArticles.Exists(article2 => article.Id == article2.Id));
            if (olderArticles.Count > ServiceFacade.SettingsHelper.RelatedArticleNewsNumber)
            {
                // Nếu số bài viết lớn hơn số bài trong config thì chỉ lấy đủ số bài
                olderArticles = olderArticles.GetRange(0, ServiceFacade.SettingsHelper.RelatedArticleNewsNumber);
            }
        }

        public Article Get(string alias, string language)
        {
            var article = DataRepository.ArticleProvider.GetByAlias(alias);
            HtmlDecode(article);
            return article;
        }

        public Article Get(string alias)
        {
            var article = DataRepository.ArticleProvider.GetByAlias(alias);
            HtmlDecode(article);
            return article;
        }

        public Article Get(int id)
        {
            var article = DataRepository.ArticleProvider.GetById(id);
            HtmlDecode(article);
            return article;
        }

        public TList<Article> GetAll()
        {
            var articles = DataRepository.ArticleProvider.GetAll();
            foreach (var article in articles)
            {
                HtmlDecode(article);
            }
            return articles;
        }

        public TList<Article> GetByMenuId(int id)
        {
            var articles = DataRepository.ArticleProvider.GetByMenuId(id);
            foreach (var article in articles)
            {
                HtmlDecode(article);
            }
            return articles;
        }
    }
}
